
  
var flatten = function(root) {
  var nodes = [], i = 0;

  function recurse(node) {
    if (node.children) node.size = node.children.reduce(function(p, v) { return p + recurse(v); }, 0);
    if (!node.id) node.id = ++i;
    nodes.push(node);
    return node.size;
  }

  root.size = recurse(root);
  return nodes;
}



function new_force(w,h){

  var force = d3.layout.force(); // 建立 Layout
  this.nodes = force.nodes();
  this.links = force.links();
  this.text = d3.selectAll("text")
  this.tmp_nodes = []
  this.tmp_links = []
  this.selected = {
    click_node :null,
    x: null,
    y: null
  }
  this.net_data = null;
  var color = d3.scale.category20();
  var link_container = null;
  var node_container = null;
  var zoom = null
  
  var parmeters = {
    node: {
      r:5,
      width: "1px"
    },
    link:{
      value:70,
      stroke: '#ccc',
      'stroke-width': '1px'
    }
    
  }
  var instance = this;
  // nodes = ss.n
  // links = ss.l

  
  this.get_data = function(){
    return instance.net_data;
  }

  this.test = function(){


    for(var i = 0; i < 10 ; i ++ ) {
        instance.add_node({
          id: i,
          text: i+"1"
        }, true);
    }
    
    // d3.range(this.nodes.length - 1).map(
    //   function(i) {
    //     instance.links.push({
    //       source: Math.floor(Math.sqrt(i)),
    //       target: i + 1 ,
    //       value: i
    //     });
    // });
  }

  this.test_links = function(){

  }


  
  var init = function(){
    zoomed =  function () {
      container
        .attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
      // container.selectAll("circle")
      // .attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");

    }

    zoom = d3.behavior.zoom()
      .scaleExtent([0.1, 10])
      .on("zoom", zoomed);

    container = d3.select("body").select("svg").append("g");    
    link_container = container.append("g").attr("class", ".lines");
    node_container = container.append("g").attr("class", ".circles");
    d3.select("svg").call(zoom);
    
  }
  

/*
*********************************************
    action area
*********************************************
*/

  var mouseover = function(d, i){
    indexs = instance.find_nearby(d);
    indexs.push(d.index);
    d3.selectAll("circle")
        .style("stroke", function(d ){
          if (indexs.includes(d.index )){
            return "#6798d2";
          }
          if(d.selected){
            return "#6798d2";
          }
        });

    d3.selectAll("circle")
      .style("r", function(d) { 
        if (d.index == i){
          return d.r? d.r + 4 : 12;   
        }else{
          return d.r ? d.r : 5;
        }
      });

   

    ls = instance.find_links(d)
    ls.forEach(function(l){
      l.near = true;
    })
    ns = instance.get_ele("text", d)
    ns.forEach(function(ele){
      instance.show_text(ele,d,  true)
    })
    console.log(ns);
    // ns.forEach()

      
    // instance.others_to_tmp(d);
    
    

    // force.linkDistance(function(l){
    //   return l.near? 70 : 30;
    // }).start();    
  }

  var mouseout = function(d, i){
    d3.selectAll("circle")
      .style("stroke", function(e){ 
        return color(e.index);
      });


    d3.selectAll("circle")
      // .delay(function(d, i) { return i * 10; })
      .style("r", function(d) { 
        if (d.selected){
          return d.r;
        }
        if (d.index == i){
          return d.r;   
        }
      }).style("stroke-width", function(x){ 
        return x.width
      })

    
    ns = instance.get_ele("text", d)
    ns.forEach(function(ele){
      instance.show_text(ele, d, false)
    })

    // instance.tmp_callback();

    force.links().forEach(function (d){
      d.near = false;
    });


    // force.linkDistance(function(l){
    //   return l.near? 70: 30;
    // }).start();


  }


  var onclick = function(d,i){
    if (instance.selected.click_node){
      if (instance.selected.click_node == d){

      }else{
        instance.link_to(d, instance.selected.click_node)
        instance.link_to(d, instance.selected.click_node)
        console.log(d.name )
        console.log(instance.selected.click_node.name)

      }
      d3.select(this).style("stroke-width", 1);
      instance.selected.click_node.selected = false;
      instance.selected.click_node.r = parmeters.node.r;
      instance.selected.click_node.width = "1px"
      instance.selected.click_node = null;
      

    }else{

      instance.selected.click_node = d
      
      d3.select(this)
        .style("stroke-width", "3px");
      instance.selected.click_node.r = d.r * 3;
      instance.selected.click_node.width = "3px";
      instance.selected.click_node.selected = true;
    }
    // console.log("text." + d.id)
    // instance.show_ele_info( "text." + d.id ,d);

  }
 
// container.call("zoom", zoom);

/*
*********************************************
    UI/ INFO function area
*********************************************
*/
  this.append_ui = function(ele, attr, func){
    // console.log("append ", ele)
    
    // return container.selectAll(ele)
    //   .data(array)
    //   .append(ele)
    //   .attr("x", function(d){
    //     return d.x
    //   })
    //   attr("y", function(d){
    //     return d.y
    //   })
  }

  this.show_text = function(ele,in_data, show_or_hide){
    // console.log(data.attr)
     
     d3.select(ele)
      .attr("x", function(data){
        if (data.x < in_data.x){
          return in_data.x -300 + (data.x - in_data.x) * 1.5
        }else if (data.x > in_data.x){
          return in_data.x +200 + (data.x - in_data.x);
        }else{
          return data.x;
        }

      })
      .attr("y", function(data){
        return data.y + (data.y - in_data.y)*2 + (data.x - in_data.x );
        
      })
      .style("fill-opacity",function(x){
        return show_or_hide? "1.0" : "0.0"
      })
      // console.log(ms)
  }

  this.ui_select = function(ele, attr, func){
    container.selectAll(ele)
      .attr(attr, function(d){ return func(d)})
  }
/*
*********************************************
    update function 
*********************************************
*/
  this.update_from_json = function(url){
    var data = null
    d3.json(url, function(data){
      // instance.net_data = data.children
      n = flatten(data);
      l = d3.layout.tree().links(n);
      
      for( var i in n){
      
        instance.add_node(n[i]);
        update()  
        
      }
      // for(var i in l){
        
      //   instance.add_link(l[i])
      // }
      
      update()  
      instance.keepNodesOnTop()
      
    })
    // setTimeout(instance.updateAll(), 1000)
    // this.updateAll()
    return data;
  }

  var update = function(){
    link = d3.layout.tree().links(instance.nodes);
    instance.add_links(link);
    link = link_container.selectAll("line")
            .data(instance.links, function(d){ return d.source.id + "-" + d.target.id;});
    link.enter().append("line")
      .style("stroke","#ccc")
      .attr("id",function(d){ return d.source.index + "-" + d.target.index});
    link.exit().remove()
    
    node = node_container.selectAll("circle")
      .data(instance.nodes, function(d){ return d.id });
    node.enter().append("circle")
        .attr("class","node")
        .style("fill", "#fff")
        .attr("stroke", function(d){ return  color(d.id)})
        .on("mouseover", mouseover)  
        .on("mouseout", mouseout)
        .on("click", onclick)
        .call(force.drag);
        // .attr("stroke-with", 2)
        
        
        

    info = node.enter().insert("g")
        .attr("class", function(d){ return d.id })
        .attr("id", "info")
        .attr("fill-opacity", "0.0")
    
    text = info.append("text")
        .attr("id","tag")
        .text(function (d) {
            return d.attr;
        });

    node.exit().remove();

    var tick = function () { // tick 會不斷的被呼叫
      // nodeEnter.attr({
        
        
      // }); 

      link.attr({
        x1 :function(d){ return d.source.x; },
        y1 :function(d){ return d.source.y; },
        x2 :function(d){ return d.target.x; },
        y2 :function(d){ return d.target.y }
      });

      node.attr({
        cx: function(it) { return it.x; },  // it.x 由 Force Layout 提供
        cy: function(it) { return it.y; },  // it.y 由 Force Layout 提供
        r: parmeters.node.r,
        // "transform": function(d) {
        //   return "translate(" + d.x + "," + d.y + ")";
        // }
      })

      info.attr({
        x: function(it) { return it.x; },  // it.x 由 Force Layout 提供
        y: function(it) { return it.y; },  // it.y 由 Force Layout 提供
      })


      
    }

    force.on("tick", tick)           // 設定 tick 函式   
      .charge(-200)
      .size([w,h])            // 設定範圍
      .linkDistance(function(l){
        return  l.value? l.value :30;
      })
      .start(); 

    instance.keepNodesOnTop()
  }


/*
*********************************************
    function area
*********************************************
*/
  
  this.get_ele = function(ele, data){
    m = []
    ns = instance.get_nearby_nodes(data);
    console.log(ns.length)
    d3.selectAll(ele).
      attr("fill-opacity", function(D){
        if(ns.includes(D)){
          m.push(this)
          return "1.0"
        }
        
      })
    console.log(m.length)
    return m;
  }

  this.link_near_do = function(ele, data, attr, func){
    ls = instance.find_nearby_links(data)
    // console.log(ls)
    return d3.selectAll(ele)
            .style(attr, function(d){   
              if(ls.includes(d)){
                return func(d); 
              }else{
                return parmeters.link[attr]
              }
            });
  }

  this.link_others_do = function(ele, data, attr, func){
    
    ls = instance.find_others_links(data)
    return d3.selectAll(ele)
            .style(attr, function(d){
              if(ls.includes(d.index)){
                return func(d); 
              }else{
                return parmeters.link[attr]
              }
            });
  }

  this.get_nearby_nodes =function(node){
    ls = [node]
    instance.links.forEach(
      function(d){
        if (d.source  == node){
          ls.push(d.target);
        }else if(d.target == node){
          ls.push(d.source);
        }
    })
    return ls;
  }

  this.remove_node_by_id = function (id){
      node = instance.find_node_by_id (id);
      for(var n in node){
        instance.nodes.pop(n);  
      }
      update()
  }

  this.remove_node = function (inx){
    node = find_node(inx);
    instance.nodes.pop(node);
    ls =  find_links(node);
    ls.forEach(function(d){
      instance.links.pop(d);
    })
    update();
  }



  this.find_nearby = function (node){
    inx = node.index;
    links = force.links()
    indexs = []
    links.forEach(function(d){
      if (d.source.index == inx){
        // console.log(d.target.index)
        indexs.push(d.target.index)
        indexs.push(d.source.index)
      }else if (d.target.index == inx){
        indexs.push(d.source.index)
      }
    })

    return indexs;
  }

  this.others_to_tmp = function(d){
    ns = []
    ls = instance.find_others_links(d, function(x){
      instance.tmp_links.push(x);
      instance.tmp_nodes.push(x.source)
      instance.tmp_nodes.push(x.target)

      instance.links.pop(x)
      instance.nodes.pop(x.source)
      instance.nodes.pop(x.target)
    });
    update()
  }

  this.tmp_callback = function(){
    instance.tmp_nodes.forEach(function(d){
      instance.nodes.push(d);
    })
    instance.tmp_links.forEach(function(l){
      instance.links.push(l);
    })
    update()
  }

  this.find_node = function (i){
    var t_node = null
    force.nodes().forEach(function(d){
      if (d.index == i){
        t_node = d;
      }
    })
    return t_node;
  }

  this.find_links = function (node){
    var l = []
    force.links().forEach(function(D){
      if (D.source == node){
        l.push(D);
      }
      if (D.target == node){
        l.push(D);
      }
    })
    return l;
  }


  this.remove_node = function (inx){
    node = find_node(inx);
    this.nodes.pop(node);
    ls =  find_links(node);
    ls.forEach(function(d){
      this.links.pop(d);
      this.nodes.pop(d.target);
      this.nodes.pop(d.source);
    })
    update();
  }

  this.find_others_links = function(node, fun){
    ls = []
    force.links().forEach(function(l){
      if(l.source.index != node.index && l.target.index != node.index){

        if (fun){
          fun(l);  
        }
        ls.push(l);
      }
    })
    return ls
    
  }

  this.find_nearby_links = function(node, fun){
    ls = []
    force.links().forEach(function(l){
      if(l.source == node || l.target == node){
        if (fun){
          fun(l);  
        }
        ls.push(l);
      }
    })
    return ls
    
  }

  this.add_node = function(node, if_update){
    // init all node
    for (var k in parmeters.node ){
      node[k] = parmeters.node[k]
    }
    //
    instance.nodes.push(node)
    if (if_update){
      update()
    }
  }

  this.add_link = function(link, if_update){
    for (var k in parmeters.link ){
      link[k] = parmeters.link[k]
    }
    //
    instance.links.push(link)
    if (if_update){
      update()
    }
  }

  this.add_links = function(link_all){
    for(var i in link_all){
      instance.add_link(link_all[i], false);
    }
  }

  this.add_node_to = function (inx,node){
    n = this.find_node(inx);
    tmp = this.find_node(node.id);
    if_true = true;

    new_link = { source:n , target:node}
    this.nodes.push(node);
    this.links.push(new_link);
    update();
    return if_true;
  }


  this.add_link_by_id = function(s_id, t_id, v, if_update){
    var new_link;
    try{
      new_link = { 
        source: instance.find_node_by_id(s_id),
        target: instance.find_node_by_id(t_id),
        value: v
      };
    }catch (err){
      // console.log(err)
      new_link = null;
      return;
    }

    if (new_link){
      instance.links.push(new_link)
    }

    if (if_update){
      update();
    }

  }

  this.find_node_by_id = function (id) {
    ns = []
    for (var i in instance.nodes) {
      if (instance.nodes[i]["id"] === id){
        ns.push( instance.nodes[i]);
      } 
        
    }
    return ns;
  };

  this.link_to = function(s_node, to_node){
    l = {source:s_node , target: to_node}
    for(var k in parmeters.link){
      l[k] = parmeters.link[k]
    }

    instance.links.push(l);
    update()
  }

  this.updateAll = function(){
    update()
  }

  this.keepNodesOnTop = function () {
        $(".node").each(function( index ) {
            var gnode = this.parentNode;
            gnode.parentNode.appendChild(gnode);
        });
    }



  init();
  update()
  instance.update_from_json("b.json")
  


}
